home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus 2000 #4
/
Amiga Plus CD - 2000 - No. 4.iso
/
Vollversion
/
CamD
/
development
/
docs
/
drivers.doc
< prev
next >
Wrap
Text File
|
2000-05-15
|
10KB
|
202 lines
How to write a hardware driver for CAMD.
CAMD drivers are fairly simple entities. Each one consists of a single
load file (i.e. a file which can be loaded with LoadSeg), and lives in
DEVS:midi. The "MidiPorts" preferences editor is capable of finding all
of the drivers that are in that directory.
The load file for the driver begins with special sequence of code and
data, similar to the RomTag structure used for disk-based libraries.
The first thing in the driver is the instruction sequence:
MOVEQ #0,d0
RTS
The prevents the driver from crashing if anybody tries to run it
as an executable. Note that other instruction sequences may be subsituted,
as long as they are exactly the same length.
After the two instructions comes the MidiDeviceData structure:
struct MidiDeviceData
{
ULONG Magic; /* MDD_Magic */
char *Name; /* driver name */
char *IDString;
UWORD Version;
UWORD Revision;
BOOL (*Init)(void); /* called after LoadSeg() */
void (*Expunge)(void); /* called before UnLoadSeg() */
struct MidiPortData *(*OpenPort)();
void (*ClosePort)();
UBYTE NPorts; /* number of ports */
UBYTE Flags; /* currently none */
};
#define MDD_Magic ((ULONG)'M' << 24 | (ULONG)'D' << 16 | 'E' << 8 | 'V')
Explanation of fields:
Magic -- must be the character string 'MDEV'.
Name -- pointer to the name of the driver. This should be the same as
the filename, including upper/lower case.
IDString -- This is the "full" name of the driver. This need not be
the same as the driver file name. (For example, the driver
"checkpoint" has an IDString of "Checkpoint Serial Solution").
NOTE: Because the driver format was created before Amiga version
strings were standardized, there is no "version string" field,
however it is suggested that each driver incorporate a version
string using the standard techniques. (Unfortunately, the driver
format cannot be radically changed because at least one music
application has been using the drivers directly long before the
CAMD library itself was finalized).
Version, Revision -- same as for libraries.
Init -- this is a function pointer to the initialization routine
for the driver. This routine initializes any global data
needed by the driver, finds the address of the hardware
expansion board (and determines if multiple boards are installed),
and patches the NPorts fields to indicate the number of
ports available.
This function takes no parameters.
Expunge -- this is a function pointer that de-allocates all global
data allocated by the Init routine.
This function takes no parameters.
OpenPort -- this is a pointer to a function that opens a single
hardware port and returns a pointer to a MidiPortData structure,
which is created by the driver. (Note that this structure may
be created either dynamically at the time of the call, or may
be statically allocated in the driver seglist).
This function may also lock any hardware resources needed to
gain exclusive access to the hardware, and may program
interrupts and baud rates for the port.
The function returns TRUE if the initialization succeeded,
otherwise it returns FALSE. CAMD will expect to receive
MIDI data at any time after this function succeeds.
The parameters to this function are described in detail below.
ClosePort -- this is a pointer to a function that closes a single
midi port. It may also free hardware resources if needed.
The parameters to this function are described in detail below.
The OpenPort Function
~~~~~~~~~~~~~~~~~~~~~
This function takes the following parameters:
register type explanation
~~~~~~~~ ~~~~ ~~~~~~~~~~~
a3 struct MidiDeviceData * pointer to MidiDeviceData
d0 LONG port number to open
a0 (void *)() serial transmit handler
a1 (void *)() serial receive handler
a2 APTR userdata for handlers
The OpenPort call returns a pointer to the following structure in d0:
struct MidiPortData
{
/* function to activate transmitter interrupt when idle */
void (*ActivateXmit)();
};
The serial transmit handler and serial recieve handler are function
pointers to functions inside of camd.library which handle the transfer
of midi data. When CAMD calls the OpenPort function, it will supply
these pointers.
The general scheme is that the driver should call CAMD whenever it
has recieved more data, or when it is ready to send more data. This
allows CAMD to handle the details of buffering and timestamping.
Each function call transfers a single byte of data.
The serial receive handler is called whenever a new byte of data is
received from the hardware. Register a2 should contain the userdata which
was passed to the OpenPort call. Register d0 should contain both the
received byte and a status code. Bits 0-7 will contain the received byte.
Bit 15 will be set if an overrun error occured, in other words, if
data was lost. Bits 8-14 should be zero. Bits higher than 15 are ignored,
and can be anything.
The serial transmit handler should be called whenever the hardware is
ready to accept another outgoing byte. When calling this function,
register a2 should be set to the userdata which was passed to the OpenPort
call. The results will be returned in register d0 and d1. Bits 0-7
of d0 will contain the byte to be transmitted. (Bits 8-15 will
be cleared). Register d1 will be zero if CAMD has more data available
to send in it's buffer, or it will be non-zero if the buffer is empty.
In the latter case (buffer empty), the driver should send the byte in
d0 (which was the last byte remaining in the buffer), and then temporarily
suspend output processing, in other words do _not_ call the serial
transmit function again until CAMD has indicated that it has some data
in the buffer again. CAMD will do this by calling the ActivateXMit()
function using the function pointer in the MidiPortData structure.
Register A0 will contain the address of the MidiPortData structure
which was returned from OpenPort.
An example of how this works: The internal amiga serial driver
uses an interrupt for writing to the hardware. Whenever a byte is
sent out, an interrupt is generated which indicated that the hardware
is ready to accept another byte. The driver calls the serial handler
function to get the next byte from CAMD and then writes it to the
hardware. Then, it checks the "last byte" flag in d1. If the flag is
set, it disabled the interrupt. Note that the interrupt is not cleared,
merely disabled -- when the byte that was pending in the hardware
is sent out, the interrupt signal will still be generated, but it
will have no effect because it is disabled. When CAMD gets more data
to send, it calls the driver's ActivateXMit function, which re-enables
the interrupt. At this point, the interrupt which was pending goes off,
causing the driver to once again call CAMD to get another byte from it's
buffer.
Note that ActivateXMit may be called even if the driver is already
activated, i.e. the "last byte" flag was never set.
The ClosePort Function
~~~~~~~~~~~~~~~~~~~~~~
This function takes the following parameters:
register type explanation
~~~~~~~~ ~~~~ ~~~~~~~~~~~
a3 struct MidiDeviceData * pointer to MidiDeviceData
d0 LONG port number to close
Writing drivers for serial cards
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Obviously, the CAMD driver mechanism is optimized for this type of
device. Each byte is sent one at a time, and the driver need not know
anything about MIDI (since it is assumed that the serial output is
connected to a hardware device which is capable of parsing MIDI streams).
We have tried to make as few assumptions as possible about the
interface to the hardware. In our example drivers, one has a seperate
interrupt for reading and writing, while the other has a single interrupt
that handles reading and writing for all ports.
In addition, in cases where the hardware provides little or no FIFO
buffering, it is possible to process multiple bytes in a single interrupt
by polling the hardware. This avoids the overhead of a 68000 interrupt and
can increase performance and reduce overrun errors.
Writing drivers for other kinds of devices
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In many cases, you will want to write a driver for a device that has no
native understanding of MIDI, such as a DSP card. In this case, it is
probably advisable that either the driver parse the MIDI stream itself,
or place the incoming bytes into a software FIFO buffer, where they can be
read and parsed by a task dedicated to that particular hardware. You can
refer to the documentation file "middbasics.doc" for more information on
understanding MIDI.